﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace HITs
{
    class CoPhieu
    {
        #region --------- Field ---------------------
        private string _tenCoPhieu;
        private List<NgayGiaoDich> _danhSachNgayGiaoDich;
        private float _diemChiSo;
        private List<float> listZscore;
        private string _maNganh;
        private List<float> listPrice_DieuChinh;
        #endregion

        #region --------- Constructors --------------
        public CoPhieu(string tenCoPhieu, List<NgayGiaoDich> danhSachNgayGiaoDich)
        {
            listPrice_DieuChinh = new List<float>();
            _tenCoPhieu = tenCoPhieu;
            //_danhSachNgayGiaoDich = danhSachNgayGiaoDich;

            _danhSachNgayGiaoDich = new List<NgayGiaoDich>();                               /// copy giá trị, tránh dùng địa chỉ(tuy tiết kiệm không gian nhưng tốn thời gian tính toán)
            foreach (NgayGiaoDich ngayGD in danhSachNgayGiaoDich)
            {
                _danhSachNgayGiaoDich.Add(new NgayGiaoDich(ngayGD.NgayGiaoDich_Str, ngayGD.GiaMoCua, ngayGD.GiaDongCua, ngayGD.GiaCaoNhat
                    , ngayGD.GiaThapNhat, ngayGD.KhoiLuongGiaoDich));
            }
            listZscore = new List<float>();
            _maNganh = "";
        }

        public CoPhieu()
        {
            _tenCoPhieu = "";
            _danhSachNgayGiaoDich = new List<NgayGiaoDich>();
            listZscore = new List<float>();
            _maNganh = "";
            listPrice_DieuChinh = new List<float>();
        }

        public CoPhieu(string tenCoPhieu)
        {
            _tenCoPhieu = tenCoPhieu;
            _danhSachNgayGiaoDich = new List<NgayGiaoDich>();
            listZscore = new List<float>();
            _maNganh = "";
            listPrice_DieuChinh = new List<float>();
        }
        #endregion

        #region --------- Methods -------------------

        #region *** Xử lý - Thao tác cơ bản

        public void Xuly_TaoTheoDanhMuc(List<float> listPercent, List<List<NgayGiaoDich>> listNgayGiaoDich, int soNgayMAX = 300)
        {
            _danhSachNgayGiaoDich = new List<NgayGiaoDich>();
            float open, close, max, min;            
            List<int> soluongCPNamGiu = new List<int>();
            string date;
            int money = 100000;
            int vol = money / 10;
            soNgayMAX--;
            for (int i = 0; i < listPercent.Count; i++)
                soluongCPNamGiu.Add((int)Math.Round(money * listPercent[i] / listNgayGiaoDich[i][soNgayMAX].GiaDongCua, 0));

            for (int j = 0; j < soNgayMAX - 1; j++)
            {
                date = listNgayGiaoDich[0][j].NgayGiaoDich_Str;
                open = 0f; close = 0f; max = 0f; min = 0f;
                for (int i = 0; i < listPercent.Count; i++)
                {
                    open += listNgayGiaoDich[i][j].GiaMoCua * soluongCPNamGiu[i];
                    close += listNgayGiaoDich[i][j].GiaDongCua * soluongCPNamGiu[i];
                    max += listNgayGiaoDich[i][j].GiaCaoNhat * soluongCPNamGiu[i];
                    min += listNgayGiaoDich[i][j].GiaThapNhat * soluongCPNamGiu[i];                  
                }

                open = (float)Math.Round(open / vol, 1);
                close = (float)Math.Round(close / vol, 1);
                max = (float)Math.Round(max / vol, 1);
                min = (float)Math.Round(min / vol, 1);
                _danhSachNgayGiaoDich.Add(new NgayGiaoDich(date, open, close, max, min, vol));
            }
        }

        /// <summary>
        /// Tính giá tổng hợp dựa trên các hệ số của giá: mở cửa, đóng cửa, cao nhất và thấp nhất
        /// </summary>
        /// <param name="HeSoMoCua"></param>
        /// <param name="HeSoDongCua"></param>
        /// <param name="HeSoCaoNhat"></param>
        /// <param name="HeSoThapNhat"></param>
        /// <returns></returns>
        public List<float> Xuly_LayDanhSachGiaTongHop(float HeSoMoCua, float HeSoDongCua, float HeSoCaoNhat, float HeSoThapNhat)
        {
            List<float> result = new List<float>();
            foreach (NgayGiaoDich ngaygiaodich in _danhSachNgayGiaoDich)
                result.Add(ngaygiaodich.TinhGiaTongHop(HeSoMoCua, HeSoDongCua, HeSoCaoNhat, HeSoThapNhat));
            return result;
        }

        /// <summary>
        /// Tính giá tổng hợp dựa trên các hệ số của giá: mở cửa, đóng cửa, cao nhất và thấp nhất (lấy từ CoreFunctions)
        /// </summary>
        /// <returns></returns>
        public List<float> Xuly_LayDanhSachGiaTongHop()
        {
            List<float> result = new List<float>();
            foreach (NgayGiaoDich ngaygiaodich in _danhSachNgayGiaoDich)
                result.Add(ngaygiaodich.TinhGiaTongHop());
            return result;
        }

        /// <summary>
        /// Tính giá trị giao dịch bằng khối lượng giao dịch * giá tổng hợp (tính theo hệ số từ CoreFunctions)
        /// </summary>
        /// <returns></returns>
        public List<float> Xuly_LayDanhSachGiaTriGiaoDich()
        {
            List<float> listGiaTongHop = this.Xuly_LayDanhSachGiaTongHop();
            List<int> listKhoiLuongGiaoDich = this.DanhSachKhoiLuongGiaoDich_Int;
            List<float> result = new List<float>();

            for (int i = 0; i < listGiaTongHop.Count; i++)
                result.Add(listGiaTongHop[i] * listKhoiLuongGiaoDich[i]);
            return result;
        }

        public List<string> Xuly_LayNgayGiaoDich()
        {
            List<string> listResult = new List<string>();

            foreach (NgayGiaoDich ngd in _danhSachNgayGiaoDich)
                listResult.Add(ngd.NgayGiaoDich_Str);
            return listResult;
        }

        /// <summary>
        /// Thêm 1 ngày giao dịch vào danh sách các ngày giao dịch
        /// </summary>
        /// <param name="ngayGiaoDich"></param>
        public void Xuly_ThemNgayGiaoDich(NgayGiaoDich ngayGiaoDich)
        {
            if (ngayGiaoDich != null)
                _danhSachNgayGiaoDich.Add(ngayGiaoDich);
        }

        /// <summary>
        /// Rút danh sách các ngày giao dịch với ngày bắt đầu và ngày kết thúc. 
        /// ngày bắt đầu là ngày gần với hiện tại hơn
        /// </summary>
        /// <param name="ngayBatDau"></param>
        /// <param name="ngayKetThuc"></param>
        /// <returns></returns>
        public List<NgayGiaoDich> Xuly_RutDanhSachNgayGiaoDich(string ngayBatDau, string ngayKetThuc)
        {
            List<NgayGiaoDich> result = new List<NgayGiaoDich>();
            foreach (NgayGiaoDich ngayGD in _danhSachNgayGiaoDich)
            {
                if (CoreFunctions.KiemTra_NgayNamGiuaBatDauVaKetThuc(ngayGD.NgayGiaoDich_Str, ngayBatDau, ngayKetThuc) == true)
                    result.Add(ngayGD);
                else
                    if (int.Parse(ngayGD.NgayGiaoDich_Str) < int.Parse(ngayKetThuc))
                        break;
            }
            return result;
        }

        public float Xuly_TangTruongGiaDongCua(string ngayBatDau, string ngayKetThuc)
        {
            List<NgayGiaoDich> result = Xuly_RutDanhSachNgayGiaoDich(ngayBatDau, ngayKetThuc);
            float batdau = result[result.Count - 1].GiaDongCua;
            float ketthuc = result[0].GiaDongCua;
            float value = (ketthuc - batdau) / batdau;
            value = (float)Math.Round(value, 3);
            return value;
        }

        /// <summary>
        /// Sắp xếp danh sách ngày giao dịch theo DES
        /// </summary>
        public void Xuly_SapXepTheoNgayGiaoDich()
        {
            _danhSachNgayGiaoDich.Sort(NgayGiaoDich.CompareNgayGiaoDich_NgayGiaoDich_DES);
        }

        public void Xuly_LoaiBoNgayGiaoDichGanNhat()
        {
            if (this._danhSachNgayGiaoDich.Count > 0)
                this._danhSachNgayGiaoDich.RemoveAt(0);
        }

        public void Xuly_AddNewZscore(string ngayGiaoDich, float Zscore)
        {
            int indexNgayGiaoDich = DanhSachNgayGiaoDich_String.IndexOf(ngayGiaoDich);
            for (int i = listZscore.Count; i < indexNgayGiaoDich; i++)
            {
                listZscore.Add(-10f);
            }

            listZscore.Add(Zscore);
        }

        public float Xuly_TangTruongGia(int index, int soNgayChechLech)
        {
            float result = 0f;
            result = (DanhSachGiaDongCua[index] - DanhSachGiaDongCua[index + soNgayChechLech]) / DanhSachGiaDongCua[index + soNgayChechLech];
            result = (float)Math.Round(result * 100, 2);
            return result;
        }

        /// <summary>
        /// 0: listGiaMoCua. 1: listGiaCaoNhat. 2: listGiaThapNhat. 3: listGiaDongCua
        /// </summary>
        /// <returns> ///0: listGiaMoCua
        ///1: listGiaCaoNhat
        ///2: listGiaThapNhat
        ///3: listGiaDongCua</returns>
        public List<List<float>> Xuly_DieuChinhGia()
        {
            ///0: listGiaMoCua
            ///1: listGiaCaoNhat
            ///2: listGiaThapNhat
            ///3: listGiaDongCua
            ///Điều chỉnh giá sẽ xử lý nếu có xuất hiện chêch lệch giữa giá đóng cửa hôm trước và giá mở cửa hôm sau.
            ///Lấy giá ngày trước để tính
            List<List<float>> listResult = new List<List<float>>();
            int length = this.DanhSachGiaCaoNhat.Count;
            float heso, gia_tham_chieu, gia_mo_cua;
            
            List<float> listGiaMoCua = new List<float>();
            List<float> listGiaCaoNhat = new List<float>();
            List<float> listGiaThapNhat = new List<float>();
            List<float> listGiaDongCua = new List<float>();

            listGiaMoCua.Insert(0, DanhSachGiaMoCua[length - 1]);
            listGiaCaoNhat.Insert(0, DanhSachGiaCaoNhat[length - 1]);
            listGiaThapNhat.Insert(0, DanhSachGiaThapNhat[length - 1]);
            listGiaDongCua.Insert(0, DanhSachGiaDongCua[length - 1]);
            
            for (int i = length - 2; i >= 0; i--)
            {
                if (DanhSachGiaMoCua[i] == listGiaDongCua[0])
                {
                    listGiaMoCua.Insert(0, DanhSachGiaMoCua[i]);
                    listGiaCaoNhat.Insert(0, DanhSachGiaCaoNhat[i]);
                    listGiaThapNhat.Insert(0, DanhSachGiaThapNhat[i]);
                    listGiaDongCua.Insert(0, DanhSachGiaDongCua[i]);
                }
                else
                {
                    gia_tham_chieu = listGiaDongCua[0];
                    //listGiaMoCua.Insert(0, gia_tham_chieu);

                    if (DanhSachGiaMoCua[i] != 0.0f && (DanhSachGiaDongCua[i + 1] - DanhSachGiaMoCua[i]) / DanhSachGiaDongCua[i + 1] > 0.1f)
                        gia_mo_cua = DanhSachGiaMoCua[i];
                    else
                        gia_mo_cua = DanhSachGiaDongCua[i + 1];

                    listGiaMoCua.Insert(0, gia_mo_cua);

                    heso = DanhSachGiaCaoNhat[i] / gia_mo_cua;
                    listGiaCaoNhat.Insert(0, (float)Math.Round(heso * gia_tham_chieu, 1));

                    heso = DanhSachGiaThapNhat[i] / gia_mo_cua;
                    listGiaThapNhat.Insert(0, (float)Math.Round(heso * gia_tham_chieu, 1));

                    heso = DanhSachGiaDongCua[i] / gia_mo_cua;
                    listGiaDongCua.Insert(0, (float)Math.Round(heso * gia_tham_chieu, 1));
                }
            }

            listResult.Add(listGiaMoCua);
            listResult.Add(listGiaCaoNhat);
            listResult.Add(listGiaThapNhat);
            listResult.Add(listGiaDongCua);

            return listResult;
        }
        #endregion

        #region *** Compare cổ phiếu
        public static int CompareCoPhieu_TenCoPhieu_ASC(CoPhieu a, CoPhieu b)
        {
            int temp = a.TenCoPhieu.CompareTo(b.TenCoPhieu);
            if (temp > 0)
                return 1;
            else
                if (temp == 0)
                    return 0;
                else
                    return -1;
        }

        public static int CompareCoPhieu_DiemChiSo_ASC(CoPhieu a, CoPhieu b)
        {
            if (a.DiemChiSoCoPhieu > b.DiemChiSoCoPhieu)
                return 1;
            else
                if (a.DiemChiSoCoPhieu == b.DiemChiSoCoPhieu)
                    return 0;
                else
                    return -1;
        }

        public static int CompareCoPhieu_DiemChiSo_DES(CoPhieu a, CoPhieu b)
        {
            if (a.DiemChiSoCoPhieu > b.DiemChiSoCoPhieu)
                return -1;
            else
                if (a.DiemChiSoCoPhieu == b.DiemChiSoCoPhieu)
                    return 0;
                else
                    return 1;
        }
        #endregion

        #region *** Tính các chỉ số mở rộng (ngoài thông tin về giá và khối lượng)
        /// <summary>
        /// Tính danh sách chỉ số KLGD/(Gia*100) (giá tổng hợp:CoreFunctions)
        /// </summary>
        /// <returns></returns>
        public List<float> TinhDanhSachChiSo_KhoiLuongGiaoDichChiaGiaTongHop()
        {
            List<float> result = new List<float>();
            List<int> listKhoiLuongGiaoDich = this.DanhSachKhoiLuongGiaoDich_Int;
            List<float> listGiaTongHop = this.Xuly_LayDanhSachGiaTongHop();

            for (int i = 0; i < listGiaTongHop.Count; i++)
                result.Add((float)Math.Round((float)listKhoiLuongGiaoDich[i] / (listGiaTongHop[i] * 100), 3));

            return result;            
        }

        public float TinhDanhSachChiSo_MoneyFlowIndex_MFI(int soNgay)
        {
            if (soNgay >= _danhSachNgayGiaoDich.Count)
                return 1 / 0f;
            List<float> listValue = new List<float>();
            List<float> listGia = new List<float>();
            float sumPos = 0f, sumNeg = 0f ;
            float MR, MFI, gia;
            
            for (int i = 0; i < soNgay + 1; i++)
            {
                gia = (_danhSachNgayGiaoDich[i].GiaCaoNhat
                    + _danhSachNgayGiaoDich[i].GiaThapNhat
                    + _danhSachNgayGiaoDich[i].GiaDongCua) / 3;
                listGia.Add(gia);
                listValue.Add( gia * _danhSachNgayGiaoDich[i].KhoiLuongGiaoDich);
            }
            for (int i = 0; i < soNgay; i++)
                if (listGia[i] >= listGia[i + 1])
                    sumPos += listValue[i];
                else
                    sumNeg += listValue[i];
            MR = sumPos / sumNeg;
            MFI = 100f - (100f / (1 + MR));
            return (float)Math.Round(MFI, 2);
        }

        public float TinhDanhSachChiSo_MoneyFlowIndex_MFI(int soNgay, int soNgayCachHienTai)
        {
            if (soNgay + soNgayCachHienTai >= _danhSachNgayGiaoDich.Count)
                return 1 / 0f;
            List<float> listValue = new List<float>();
            List<float> listGia = new List<float>();
            float sumPos = 0f, sumNeg = 0f;
            float MR, MFI, gia;

            for (int i = 0 + soNgayCachHienTai; i < soNgay + 1 + soNgayCachHienTai; i++)
            {
                gia = (_danhSachNgayGiaoDich[i].GiaCaoNhat
                    + _danhSachNgayGiaoDich[i].GiaThapNhat
                    + _danhSachNgayGiaoDich[i].GiaDongCua) / 3;
                listGia.Add(gia);
                listValue.Add(gia * _danhSachNgayGiaoDich[i].KhoiLuongGiaoDich);
            }
            for (int i = 0; i < soNgay; i++)
                if (listGia[i] >= listGia[i + 1])
                    sumPos += listValue[i];
                else
                    sumNeg += listValue[i];
            MR = sumPos / sumNeg;
            MFI = 100f - (100f / (1 + MR));
            return (float)Math.Round(MFI, 0);
        }

        public float TinhDanhSachChiSo_RelativeStrengIndex_RSI(int soNgay)
        {
            if (soNgay + 14 >= _danhSachNgayGiaoDich.Count)
                return 1 / 0f;
 
            List<float> Gain = new List<float>();
            List<float> Loss = new List<float>();
            int count = 0;
            float avgGain = 0f, avgLoss = 0f, RS, RSI = 0f, valueGain, valueLoss;
            if (soNgay + 14 > _danhSachNgayGiaoDich.Count)
                return 1f / 0f;
            for (int i = soNgay + 14; i > 0; i--)
            {
                if (count <= 14)
                {
                    if (_danhSachNgayGiaoDich[i - 1].GiaDongCua > _danhSachNgayGiaoDich[i].GiaDongCua)
                        Gain.Add(_danhSachNgayGiaoDich[i - 1].GiaDongCua - _danhSachNgayGiaoDich[i].GiaDongCua);
                    else
                        Loss.Add(_danhSachNgayGiaoDich[i].GiaDongCua - _danhSachNgayGiaoDich[i - 1].GiaDongCua);

                    if (count == soNgay)
                    {
                        avgGain = CoreFunctions.Tk_GetSum(Gain) / soNgay;
                        avgLoss = CoreFunctions.Tk_GetSum(Loss) / soNgay;
                        RS = avgGain / avgLoss;
                        RSI = 100f - (100f / (1 + RS));
                    }
                }
                else
                {
                    valueLoss = 0f;
                    valueGain = 0f;
                    if (_danhSachNgayGiaoDich[i - 1].GiaDongCua > _danhSachNgayGiaoDich[i].GiaDongCua)
                        valueGain = _danhSachNgayGiaoDich[i - 1].GiaDongCua - _danhSachNgayGiaoDich[i].GiaDongCua;
                    else
                        valueLoss = _danhSachNgayGiaoDich[i].GiaDongCua - _danhSachNgayGiaoDich[i - 1].GiaDongCua;
                    avgGain = (avgGain * (soNgay - 1) + valueGain) / soNgay;
                    avgLoss = (avgLoss * (soNgay - 1) + valueLoss) / soNgay;
                    RS = avgGain / avgLoss;
                    RSI = 100f - (100f / (1 + RS));
                }
                count++;
            }
            return (float)Math.Round(RSI, 2);
        }

        public float TinhDanhSachChiSo_RelativeStrengIndex_RSI(int soNgay, int soNgayCachHienTai)
        {
            if (soNgay + 14 >= _danhSachNgayGiaoDich.Count)
                return 1 / 0f;

            List<float> Gain = new List<float>();
            List<float> Loss = new List<float>();
            int count = 0;
            float avgGain = 0f, avgLoss = 0f, RS, RSI = 0f, valueGain, valueLoss;
            if (soNgay + 14 + soNgayCachHienTai > _danhSachNgayGiaoDich.Count)
                return 1f / 0f;
            for (int i = soNgay + 14 + soNgayCachHienTai; i >  soNgayCachHienTai && i < _danhSachNgayGiaoDich.Count; i--)
            {
                if (count <= 14)
                {
                    if (_danhSachNgayGiaoDich[i - 1].GiaDongCua > _danhSachNgayGiaoDich[i].GiaDongCua)
                        Gain.Add(_danhSachNgayGiaoDich[i - 1].GiaDongCua - _danhSachNgayGiaoDich[i].GiaDongCua);
                    else
                        Loss.Add(_danhSachNgayGiaoDich[i].GiaDongCua - _danhSachNgayGiaoDich[i - 1].GiaDongCua);

                    if (count == soNgay)
                    {
                        avgGain = CoreFunctions.Tk_GetSum(Gain) / soNgay;
                        avgLoss = CoreFunctions.Tk_GetSum(Loss) / soNgay;
                        RS = avgGain / avgLoss;
                        RSI = 100f - (100f / (1 + RS));
                    }
                }
                else
                {
                    valueLoss = 0f;
                    valueGain = 0f;
                    if (_danhSachNgayGiaoDich[i - 1].GiaDongCua > _danhSachNgayGiaoDich[i].GiaDongCua)
                        valueGain = _danhSachNgayGiaoDich[i - 1].GiaDongCua - _danhSachNgayGiaoDich[i].GiaDongCua;
                    else
                        valueLoss = _danhSachNgayGiaoDich[i].GiaDongCua - _danhSachNgayGiaoDich[i - 1].GiaDongCua;
                    avgGain = (avgGain * (soNgay - 1) + valueGain) / soNgay;
                    avgLoss = (avgLoss * (soNgay - 1) + valueLoss) / soNgay;
                    RS = avgGain / avgLoss;
                    RSI = 100f - (100f / (1 + RS));
                }
                count++;
            }
            return (float)Math.Round(RSI, 0);
        }

        public float TinhDanhSachChiSo_IntradayMomentumIndex_IMI(int soNgay, int soNgayCachHienTai)
        {
            float result;
            List<float> listGiaDongCua = new List<float>();
            List<float> listGiaMoCua = new List<float>();
            listGiaDongCua = this.DanhSachGiaDongCua;
            listGiaMoCua = this.DanhSachGiaMoCua;

            int dem = 0;
            while (dem < soNgayCachHienTai)
            {
                listGiaMoCua.RemoveAt(0);
                listGiaDongCua.RemoveAt(0);
                dem++;
            }
            result = Indicators.IMI(soNgay, listGiaMoCua, listGiaDongCua)[0];
            return result;
        }

        public List<float> TinhDanhSachChiSo_PSAR(int soNgay = 5, float step = 0.02f, float maxAcc = 0.2f)
        {
            List<float> result = new List<float>();
            float min = 1000f, max = -1f, EP, direction, AF, AFD;
            float max_old = 0f, direction_old = 1, AF_old = 1;

            for (int i = 0; i < DanhSachGiaThapNhat.Count; i++)
            {
                if (DanhSachGiaThapNhat[DanhSachGiaThapNhat.Count - 1 - i] < min)
                    min = DanhSachGiaThapNhat[DanhSachGiaThapNhat.Count - 1 - i];
                if (DanhSachGiaCaoNhat[DanhSachGiaCaoNhat.Count - 1 - i] > max)
                    max = DanhSachGiaCaoNhat[DanhSachGiaCaoNhat.Count - 1 - i];
            }

            EP = max - min;
            if (DanhSachGiaThapNhat[DanhSachGiaThapNhat.Count - 1 - soNgay - 1] > min)
                direction = 1;
            else
                direction = -1;

            AF = step;
            AFD = AF * EP;

            for (int i = DanhSachGiaThapNhat.Count - 1 - soNgay; i >= 0; i--)
            {
                if (direction == direction_old)
                {
                    if (direction == 1)
                    {
                        if ((min + AFD) < Math.Min(DanhSachGiaThapNhat[i + 1], DanhSachGiaThapNhat[i + 2]))
                            min = min + AFD;
                        else
                            min = Math.Min(DanhSachGiaThapNhat[i + 1], DanhSachGiaThapNhat[i + 2]);
                    }
                    else
                    {
                        if ((min + AFD) > Math.Max(DanhSachGiaCaoNhat[i + 1], DanhSachGiaCaoNhat[i + 2]))
                            min = min + AFD;
                        else
                            min = Math.Min(DanhSachGiaCaoNhat[i + 1], DanhSachGiaCaoNhat[i + 2]);
                    }
                }
                else
                    min = max;

                max_old = max;
                if (direction == 1)
                {
                    if (DanhSachGiaCaoNhat[i] > max)
                        max = DanhSachGiaCaoNhat[i];
                }
                else
                {
                    if (DanhSachGiaThapNhat[i] < max)
                        max = DanhSachGiaThapNhat[i];
                }

                EP = max - min;

                direction_old = direction;
                if (direction == 1)
                {
                    if (DanhSachGiaThapNhat[i] > min)
                        direction = 1;
                    else
                        direction = -1;
                }
                else
                {
                    if (DanhSachGiaCaoNhat[i] < min)
                        direction = -1;
                    else
                        direction = 1;
                }

                AF_old = AF;
                if (direction == direction_old)
                {
                    if (direction == 1)
                    {
                        if (max > max_old && AF < maxAcc)
                            AF = AF + step;
                    }
                    else
                        if (max < max_old && AF < maxAcc)
                            AF = AF + step;
                }
                else
                    AF = maxAcc;

                AFD = AF * EP;
                result.Insert(0, min);
            }
            return result;
        }

        public float TinhDanhSachChiSo_ChechlechZscoreGiua2NgayKeNhau(int soNgayCachHienTai)
        {
            float result;
            if (listZscore.Count != 0 && listZscore.Count > soNgayCachHienTai + 1)
                result = (float)Math.Abs(listZscore[soNgayCachHienTai] - listZscore[soNgayCachHienTai + 1]);
            else
                result = -10f;

            return result;
        }

        public float TinhVolality()
        {
            float result = -1000f;
            List<float> listThayDoiGia = CoreFunctions.Tk_GetDailyReturn(this.DanhSachGiaDongCua);
            result = (float)Math.Round(
                CoreFunctions.MR_TinhVolatility_ContinuousMethod(listThayDoiGia, 1f / listThayDoiGia.Count), 3);
            return result;
        }

        public int TinhSoNgayCachCrossEMA(int soNgayEMA1 = 13, int soNgayEMA2 = 34, bool flag_GiaDieuChinh = false)
        {
            List<float> pricesOfStock = new List<float>();
            if (flag_GiaDieuChinh)
                pricesOfStock = this.DanhSachGiaDongCua;
            else
                pricesOfStock = this.DanhSachGiaDongCua_DieuChinh;

            List<float> listEMA1 = Indicators.EMA(pricesOfStock, soNgayEMA1);
            List<float> listEMA2 = Indicators.EMA(pricesOfStock, soNgayEMA2);

            for(int i = 0 ; i < listEMA1.Count - 1 && i < listEMA2.Count - 1; i++)            
                if (listEMA1[i] > listEMA2[i] && listEMA1[i + 1] <= listEMA2[i + 1])
                    return i;
                
            return 100;
        }

        #endregion

        #region *** Testing - Testing_
        /// <summary>
        /// Test tỉ lệ cắt lỗ và chốt lời trong quãng thời gian được Load từ file. 
        /// Không test với quãng thời gian sau đó. Tính mua từ lúc đảo chiểu.
        /// Chỉ xét trên một kết quả cuối cùng: Tiền lời (không xét danh sách giao dịch)
        /// Đơn vị test mua là 1 CP
        /// </summary>
        /// <param name="soNgay">số ngày cách với điểm đảo chiều</param>
        /// <param name="soNgayT">số ngày T+X (quy định giao dịch CK)</param>
        /// <param name="phanTram">[0,100] phần trăm tăng của CP tính từ điểm đảo chiều </param>
        /// <param name="tiLeCatLo">[0,100] tỉ lệ cắt lỗ</param>
        /// <param name="tiLeChotLoi">[0,100] tỉ lệ chốt lời</param>
        /// <param name="soLanMua">số lần mua. Để tính chi phí giao dịch</param>
        /// <param name="soLanBan">số lần bán. Để tính chi phí giao dịch</param>
        /// <param name="phanTramLoi">số phần trăm lợi nhuận lời tính cả chi phi giao dịch</param>
        /// <returns>số tiền lời</returns>
        public float Testing_CatLoChotLoi_Original(int soNgay, int soNgayT, float phanTram, float tiLeCatLo, float tiLeChotLoi
            , out int soLanMua, out int soLanBan, out float phanTramLoi)
        {
            List<float> listValues = this.Xuly_LayDanhSachGiaTongHop();
            soLanMua = 0;
            soLanBan = 0;
            phanTramLoi = 0f;

            phanTram = phanTram / 100;
            tiLeCatLo = tiLeCatLo / 100;
            tiLeChotLoi = tiLeChotLoi / 100;

            int idMuaCP = -1, idBanCP = -1;
            bool flag = false;                                                                          /// false: chưa mua CP - true: đã mua CP
            float result = 0f;                                                                          /// tổng tiền lời

            for (int idbreak = listValues.Count - 2; idbreak >= 1; idbreak--)
                if (listValues[idbreak + 1] >= listValues[idbreak]
                    && listValues[idbreak - 1] >= listValues[idbreak])                                  /// listValues sắp xếp theo dạng DES. Tìm điểm đảo chiều idbreak (điểm thấp nhất giữa 2 điểm 2 bên)
                    for (int j = idbreak - 1; j >= 0; j--)                                              /// Các ngày tiếp theo kể từ ngày đảo chiều                 
                        if (listValues[j] >= listValues[j + 1])                                         /// Kiểm tra: ngày tiếp theo có xu hướng tăng hay không
                        {
                            if (idbreak - j >= soNgay
                                && (float)Math.Log(listValues[j] / listValues[idbreak]) >= phanTram)    /// Kiểm tra: ngày tiếp theo có xu hướng thỏa các điều kiện về phần trăm tăng (hàm log) và số ngày tính từ lúc đảo chiều
                                if (flag == false && (soLanBan == 0
                                    || (soLanBan > 0 && (idBanCP - j) >= soNgayT)))                     /// Kiểm tra: chưa mua CP && (chưa giao dịch lần nào hoặc đã bán thì phải đợi T ngày để hoàn tiền) thì mới mua
                                {
                                    flag = true;
                                    idMuaCP = j;                                                        /// Xác định ngày mua cổ phiếu
                                    soLanMua++;
                                }
                                else                                                                    /// Đã mua cổ phiếu và thỏa điều kiện tỉ lệ chốt lời thì bán CP                               
                                    if (flag == true && idMuaCP - j >= soNgayT
                                        && (float)Math.Log(listValues[j] / listValues[idMuaCP]) >= tiLeChotLoi)
                                    {
                                        flag = false;
                                        result += (listValues[j] - listValues[idMuaCP]);
                                        soLanBan++;
                                        phanTramLoi += (float)Math.Log(listValues[j] / listValues[idMuaCP]);
                                        idBanCP = j;
                                        idbreak = idBanCP - soNgayT + 1;                                /// Bán CP ra - cập nhật lại ngày đảo chiều
                                        break;
                                    }
                        }
                        else                                                                            /// ngày có xu hướng giảm. True: Đã mua cổ phiếu và đạt được tỉ lệ cắt lỗ                                                                      
                            if (flag == true && idMuaCP - j >= soNgayT
                                && (float)Math.Log(listValues[idMuaCP] / listValues[j]) >= tiLeCatLo)
                            {
                                flag = false;
                                result += (listValues[j] - listValues[idMuaCP]);
                                soLanBan++;
                                phanTramLoi += (float)Math.Log(listValues[j] / listValues[idMuaCP]);
                                idBanCP = j;
                                idbreak = idBanCP - soNgayT + 1;                                       /// Bán CP ra - cập nhật lại ngày đảo chiều
                                break;
                            }

            if (soLanBan != 0)
                phanTramLoi = (float)Math.Round(phanTramLoi * 100 / soLanBan, 0);
            else
                phanTramLoi = (float)Math.Round(phanTramLoi * 100, 0);

            result = (float)Math.Round(result, 3);
            return result;
        }

        /// <summary>
        /// Cải tiến hơn Testing_CatLoChotLoi_Original(...): khi vượt qua mức Chốt Lời vẫn tiếp tục duy trì cho tới khi gặp đỉnh đảo chiều tiếp theo 
        /// </summary>
        /// <param name="soNgay">số ngày cách với điểm đảo chiều</param>
        /// <param name="soNgayT">số ngày T+X (quy định giao dịch CK)</param>
        /// <param name="phanTram">[0,100] phần trăm tăng của CP tính từ điểm đảo chiều </param>
        /// <param name="tiLeCatLo">[0,100] tỉ lệ cắt lỗ</param>
        /// <param name="tiLeChotLoi">[0,100] tỉ lệ chốt lời</param>
        /// <param name="soLanMua">số lần mua. Để tính chi phí giao dịch</param>
        /// <param name="soLanBan">số lần bán. Để tính chi phí giao dịch</param>
        /// <param name="phanTramLoi">số phần trăm lợi nhuận lời tính cả chi phi giao dịch</param>
        /// <returns>số tiền lời</returns>
        public float Testing_CatLoChotLoi_Version1(int soNgay, int soNgayT, float phanTram, float tiLeCatLo, float tiLeChotLoi
         , out int soLanMua, out int soLanBan, out float phanTramLoi)
        {
            List<float> listValues = this.Xuly_LayDanhSachGiaTongHop();
            soLanMua = 0;
            soLanBan = 0;
            phanTramLoi = 0f;

            phanTram = phanTram / 100;
            tiLeCatLo = tiLeCatLo / 100;
            tiLeChotLoi = tiLeChotLoi / 100;

            int idMuaCP = -1, idBanCP = -1;

            bool flag = false;                                                                          /// false: chưa mua CP - true: đã mua CP
            float result = 0f;                                                                          /// tổng tiền lời

            for (int idbreak = listValues.Count - 2; idbreak >= 1; idbreak--)
                if (listValues[idbreak + 1] >= listValues[idbreak]
                    && listValues[idbreak - 1] >= listValues[idbreak])                                  /// listValues sắp xếp theo dạng DES. Tìm điểm đảo chiều idbreak (điểm thấp nhất giữa 2 điểm 2 bên)
                    for (int j = idbreak - 1; j >= 0; j--)                                              /// Các ngày tiếp theo kể từ ngày đảo chiều                 
                        if (listValues[j] >= listValues[j + 1])                                         /// Kiểm tra: ngày tiếp theo có xu hướng tăng hay không
                        {
                            if (idbreak - j >= soNgay
                                && (float)Math.Log(listValues[j] / listValues[idbreak]) >= phanTram)    /// Kiểm tra: ngày tiếp theo có xu hướng thỏa các điều kiện về phần trăm tăng (hàm log) và số ngày tính từ lúc đảo chiều
                                if (flag == false && (soLanBan == 0
                                    || (soLanBan > 0 && (idBanCP - j) >= soNgayT)))                     /// Kiểm tra: chưa mua CP && (chưa giao dịch lần nào hoặc đã bán thì phải đợi T ngày để hoàn tiền) thì mới mua
                                {
                                    flag = true;                                                        /// Thực hiện mua cổ phiếu
                                    idMuaCP = j;                                                        /// Xác định ngày mua cổ phiếu
                                    soLanMua++;
                                }
                        }
                        else                                                                            /// Gặp ngày có xu hướng giảm. True: Đã mua cổ phiếu và đạt được tỉ lệ cắt lỗ                                                                      
                            if (flag == true && idMuaCP - j >= soNgayT &&
                                ((float)Math.Log(listValues[idMuaCP] / listValues[j]) >= tiLeCatLo
                                || (float)Math.Log(listValues[j] / listValues[idMuaCP]) >= tiLeChotLoi))
                            {
                                flag = false;
                                result += (listValues[j] - listValues[idMuaCP]);
                                phanTramLoi += (float)Math.Log(listValues[j] / listValues[idMuaCP]);

                                idBanCP = j;                                                           /// Bán CP ra - cập nhật lại ngày đảo chiều
                                idbreak = idBanCP - soNgayT + 1;                                       /// Tips: đẩy ngày xét đảo chiều qua số ngày T để xét mua bán       

                                soLanBan++;
                                break;
                            }

            if (soLanBan != 0)
                phanTramLoi = (float)Math.Round(phanTramLoi * 100 / soLanBan, 0);
            else
                phanTramLoi = (float)Math.Round(phanTramLoi * 100, 0);

            result = (float)Math.Round(result, 3);
            return result;
        }
        #endregion

        #endregion

        #region --------- Properties ----------------
        public string TenCoPhieu
        {
            get { return _tenCoPhieu; }
        }

        public List<NgayGiaoDich> GetDanhsachNgayGiaoDich(int minIndex, int maxIndex)
        {
            List<NgayGiaoDich> result = new List<NgayGiaoDich>();
            if (this._danhSachNgayGiaoDich.Count > maxIndex)
            {
                for (int i = minIndex; i < maxIndex; i++)
                    result.Add(this._danhSachNgayGiaoDich[i]);
            }

            return result;
        }

        public List<NgayGiaoDich> DanhSachNgayGiaoDich
        {
            get { return _danhSachNgayGiaoDich; }
        }

        public List<string> DanhSachNgayGiaoDich_String
        {
            get
            {
                return Xuly_LayNgayGiaoDich();
            }
        }

        public List<int> DanhSachKhoiLuongGiaoDich_Int
        {
            get
            {
                List<int> result = new List<int>();
                foreach (NgayGiaoDich ngaygiaodich in _danhSachNgayGiaoDich)
                    result.Add(ngaygiaodich.KhoiLuongGiaoDich);
                return result;
            }
        }

        public List<float> DanhSachKhoiLuongGiaoDich
        {
            get
            {
                List<float> result = new List<float>();
                foreach (NgayGiaoDich ngaygiaodich in _danhSachNgayGiaoDich)
                    result.Add(ngaygiaodich.KhoiLuongGiaoDich);
                return result;
            }
        }

        public List<float> DanhSachGiaMoCua
        {
            get
            {
                List<float> result = new List<float>();
                foreach (NgayGiaoDich ngaygiaodich in _danhSachNgayGiaoDich)
                    result.Add(ngaygiaodich.GiaMoCua);
                return result;
            }
        }

        public List<uint> DanhSachGiaTriGiaoDich
        {
            get
            {
                List<uint> listValue = new List<uint>();
                foreach (NgayGiaoDich ngaygiaodich in _danhSachNgayGiaoDich)
                    listValue.Add((uint)(ngaygiaodich.GiaDongCua * ngaygiaodich.KhoiLuongGiaoDich));
                return listValue;
            }
        }

        public List<float> DanhSachGiaDongCua
        {
            get
            {
                List<float> result = new List<float>();
                foreach (NgayGiaoDich ngaygiaodich in _danhSachNgayGiaoDich)
                    result.Add(ngaygiaodich.GiaDongCua);
                return result;
            }
        }

        public List<float> DanhSachGiaDongCua_DieuChinh
        {
            get
            {
                if (listPrice_DieuChinh.Count == 0)
                    listPrice_DieuChinh = this.Xuly_DieuChinhGia()[3];
                
                return listPrice_DieuChinh;
            }            
        }

        public List<float> DanhSachGiaCaoNhat
        {
            get
            {
                List<float> result = new List<float>();
                foreach (NgayGiaoDich ngaygiaodich in _danhSachNgayGiaoDich)
                    result.Add(ngaygiaodich.GiaCaoNhat);
                return result;
            }
        }

        public List<float> DanhSachGiaThapNhat
        {
            get
            {
                List<float> result = new List<float>();
                foreach (NgayGiaoDich ngaygiaodich in _danhSachNgayGiaoDich)
                    result.Add(ngaygiaodich.GiaThapNhat);
                return result;
            }
        }

        public float DiemChiSoCoPhieu
        {
            get { return _diemChiSo; }
            set { _diemChiSo = value; }
        }

        /// <summary>
        /// Lấy Danh sách giá tổng hợp thay đổi theo từng ngày
        /// Log(ngày sau/ngày trước). Ngày sau là ngày gần với hiện tại hơn
        /// </summary>
        public List<float> DanhSachThayDoiGiaTongHopTungNgay
        {
            get
            {
                List<float> result = new List<float>();
                List<float> listGiaTongHop = this.Xuly_LayDanhSachGiaTongHop();

                float value;
                for (int i = 0; i < listGiaTongHop.Count - 1; i++)
                {                    
                    value = (float)Math.Round(Math.Log(listGiaTongHop[i] / listGiaTongHop[i + 1]), 3);
                    result.Add(value);
                }
                return result;
            }
        }

        /// <summary>
        /// Lấy danh sách khối lượng thay đổi theo từng ngày
        /// Log(ngày sau/ngày trước). Ngày sau là ngày gần với hiện tại hơn
        /// </summary>
        public List<float> DanhSachThayDoiKhoiLuong
        {
            get
            {
                List<float> result = new List<float>();
                List<float> listGiaTongHop = this.DanhSachKhoiLuongGiaoDich;

                float value;
                for (int i = 0; i < listGiaTongHop.Count - 1; i++)
                {
                    if (listGiaTongHop[i + 1] != 0)
                        value = (float)Math.Round(Math.Log(listGiaTongHop[i] / listGiaTongHop[i + 1]), 3);
                    else
                        value = 0f;                    
                    result.Add(value);
                }
                return result;
            }
        }

        /// <summary>
        /// Tăng trưởng giá: Chêch lệch từ giá bắt đầu và kết thúc
        /// </summary>
        public float TangTruongGia
        {
            get
            {
                List<float> listGiaTongHop = this.Xuly_LayDanhSachGiaTongHop();
                float result = 0.0f;

                if (listGiaTongHop.Count != 0)                
                    result = (float)Math.Round(Math.Abs(listGiaTongHop[0] / listGiaTongHop[listGiaTongHop.Count - 1]), 2);                
                return result;
            }
        }

        public int SoNgayGiaoDich
        {
            get
            {
                return this._danhSachNgayGiaoDich.Count;
            }
        }

        public float ThayDoiGiaTheoNgay
        {
            get
            {
                List<float> listGiaTongHop = this.Xuly_LayDanhSachGiaTongHop();
                float result = 0.0f;

                if (listGiaTongHop.Count > 1)
                    result = (float)Math.Round(100 * (listGiaTongHop[0] - listGiaTongHop[1]) / listGiaTongHop[1], 1);
                
                return result;
            }
        }

        public float ThayDoiKhoiLuongTheoNgay
        {
            get
            {
                List<float> listKhoiLuong = this.DanhSachKhoiLuongGiaoDich;
                float result = 0.0f;

                if (listKhoiLuong.Count > 8)
                {
                    float avg8 = 0f;
                    for (int i = 1; i <= 8; i++)
                        avg8 += listKhoiLuong[i];
                    avg8 /= 8;
                    result = (float)Math.Round(100 * (listKhoiLuong[0] - avg8) / avg8, 1);
                }
                else
                    result = 0.0f;
                
                return result;
            }
        }

        public override string ToString()
        {
            string text = _tenCoPhieu;
            return text;
        }

        public float GiaDongCuaTB10Ngay
        {
            get
            {
                float sum = 0; int count = 0;
                for (int i = 0; i < 10; i++)
                {
                    count++;
                    sum += DanhSachGiaDongCua[i];
                }
                return (float)Math.Round(sum / count, 2);
            }
        }

        public float KhoiLuongTB10Ngay
        {
            get
            {
                float sum = 0f; int count = 0;
                for (int i = 0; i < 10; i++)
                {
                    count++;
                    sum += DanhSachKhoiLuongGiaoDich[i];
                }
                return (float)Math.Round((float)sum / count, 0);
            }
        }

        public uint GiaTriGiaoDich10Ngay
        {
            get
            {
                List<uint> listGiaTriGiaoDich = new List<uint>(this.DanhSachGiaTriGiaoDich);
                uint sum = 0; int count = 0;
                for (int i = 0; i < 10; i++)
                {
                    count++;
                    sum += listGiaTriGiaoDich[i];
                }
                return (uint)(sum / count);
            }
        }

        public List<float> DanhsachZscore
        {
            get
            {
                return listZscore;
            }
           
        }

        public string MaNganh
        {
            get
            {
                return _maNganh;
            }
            set
            {
                _maNganh = value;
            }
        }

        #endregion

        #region --------- Compare -------------------
        public static int Compare_ThayDoiGiaTheoNgay_DES(CoPhieu A, CoPhieu B)
        {
            return -(A.ThayDoiGiaTheoNgay.CompareTo(B.ThayDoiGiaTheoNgay));
        }

        public static int Compare_ThayDoiKhoiLuongTheoNgay_DES(CoPhieu A, CoPhieu B)
        {
            return -A.ThayDoiKhoiLuongTheoNgay.CompareTo(B.ThayDoiKhoiLuongTheoNgay);
        }

        public static int Compare_TenCoPhieu_ASC(CoPhieu A, CoPhieu B)
        {
            return A.TenCoPhieu.CompareTo(B.TenCoPhieu);
        }
        #endregion
    }
}
